home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / program / misc / fpl-v13.lha / fpl / src / statement.c < prev   
C/C++ Source or Header  |  1995-08-11  |  50KB  |  2,007 lines

  1. /******************************************************************************
  2.  *                   FREXX PROGRAMMING LANGUAGE                  *
  3.  ******************************************************************************
  4.  
  5.  statement.c
  6.  
  7.  Support routines to the Expression() function.
  8.  
  9.  *****************************************************************************/
  10.  
  11. /************************************************************************
  12.  *                                                                      *
  13.  * fpl.library - A shared library interpreting script langauge.         *
  14.  * Copyright (C) 1992-1994 FrexxWare                                    *
  15.  * Author: Daniel Stenberg                                              *
  16.  *                                                                      *
  17.  * This program is free software; you may redistribute for non          *
  18.  * commercial purposes only. Commercial programs must have a written    *
  19.  * permission from the author to use FPL. FPL is *NOT* public domain!   *
  20.  * Any provided source code is only for reference and for assurance     *
  21.  * that users should be able to compile FPL on any operating system     *
  22.  * he/she wants to use it in!                                           *
  23.  *                                                                      *
  24.  * You may not change, resource, patch files or in any way reverse      *
  25.  * engineer anything in the FPL package.                                *
  26.  *                                                                      *
  27.  * This program is distributed in the hope that it will be useful,      *
  28.  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
  29.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                 *
  30.  *                                                                      *
  31.  * Daniel Stenberg                                                      *
  32.  * Ankdammsgatan 36, 4tr                                                *
  33.  * S-171 43 Solna                                                       *
  34.  * Sweden                                                               *
  35.  *                                                                      *
  36.  * FidoNet 2:201/328    email:dast@sth.frontec.se                       *
  37.  *                                                                      *
  38.  ************************************************************************/
  39.  
  40. #ifdef AMIGA
  41. #include <exec/types.h>
  42. #include <proto/exec.h>
  43. #include <stdlib.h>
  44.  
  45. #include <proto/dos.h>
  46. #include <exec/execbase.h>
  47. #include <dos.h>
  48.  
  49. #include "/funclib/funclib.h"
  50.  
  51. #elif defined(UNIX)
  52. #include <sys/types.h>
  53. #endif
  54.  
  55. #include "script.h"
  56. #include "debug.h"
  57. #include <stdio.h>
  58. #include <stddef.h>
  59. #include <limits.h>
  60.  
  61. static ReturnCode INLINE SendMessage(struct Data *, struct fplMsg *);
  62. static ReturnCode REGARGS Ltostr(struct Data *scr, struct fplStr **,
  63.                      long, long);
  64. static ReturnCode REGARGS GetSymbols(struct Data *, long, long,
  65.                                      struct fplSymbol **);
  66. static long REGARGS my_memicmp(uchar *, uchar *, long);
  67.  
  68. /**********************************************************************
  69.  *
  70.  * ReturnCode CmpAssign()
  71.  *
  72.  * Performs a compound assign to the value the third argument points to.
  73.  * The assign performed is the one with the operator specified in the fourth
  74.  * argument eg, x, +, /, &, %, | etc, etc...
  75.  *
  76.  ***************/
  77.  
  78. ReturnCode REGARGS
  79. CmpAssign(struct Data *scr,
  80.           long val,    /* right operand */
  81.       long *value,    /* return value pointer */
  82.       long flags,    /* variable flags */
  83.       uchar operation)
  84. {
  85.   ReturnCode ret;
  86.   switch(operation) { /* check the type of the assign */
  87.   case CHAR_PLUS:
  88.     *value+=val;
  89.     break;
  90.   case CHAR_MINUS:
  91.     *value-=val;
  92.     break;
  93.   case CHAR_MULTIPLY:
  94.     *value*=val;
  95.     break;
  96.   case CHAR_DIVIDE:
  97.     *value/=val;
  98.     break;
  99.   case CHAR_AND:
  100.     *value&=val;
  101.     break;
  102.   case CHAR_OR:
  103.     *value|=val;
  104.     break;
  105.   case CHAR_REMAIN:
  106.     *value%=val;
  107.     break;
  108.   case CHAR_XOR:
  109.     *value^=val;
  110.     break;
  111.   case CHAR_LESS_THAN:
  112.     *value<<=val;
  113.     break;
  114.   case CHAR_GREATER_THAN:
  115.     *value>>=val;
  116.     break;
  117.   case CHAR_ASSIGN:
  118.     *value=val;
  119.     break;
  120.   default:
  121.     CALL(Warn(scr, FPLERR_ILLEGAL_ASSIGN)); /* >warning< */
  122.     *value=val; /* perform a straight assign! */
  123.     break;
  124.   }
  125.   if(flags&FPL_VARIABLE_LESS32) {
  126.     /* if using less than 32 bit */
  127.     if(flags&FPL_CHAR_VARIABLE)
  128.       *value=(long)((signed char)*value);
  129.     else
  130.       *value=(long)((signed short)*value);
  131.   }
  132.   return(FPL_OK);
  133. }
  134.  
  135.  
  136. /**********************************************************************
  137.  *
  138.  * StrAssign();
  139.  *
  140.  * Assign a string variable.
  141.  *
  142.  ********/
  143.  
  144. ReturnCode REGARGS
  145. StrAssign(struct fplStr *app,
  146.           struct Data *scr,
  147.       struct fplStr **string,
  148.       uchar append) /* TRUE or FALSE if append */
  149. {
  150.   ReturnCode ret;
  151.  
  152.   if(!append) { /* if not append */
  153.     /* Exchange this string with the old one in the variable! */
  154.     if(*string) {
  155.       /*
  156.        * There is a string! Free any type!
  157.        */
  158.       FREE_KIND(*string);
  159.     }
  160.     if(!app) {
  161.       GETMEM(app, sizeof(struct fplStr));
  162.       memset(app, 0, sizeof(struct fplStr)); /* clean it! */
  163.     }
  164.     *string = app;
  165.  
  166.   } else { /* append string */
  167.     if(! (app?app->len:0))
  168.       /* we don't append zero length strings! */
  169.       return FPL_OK;
  170.  
  171.     CALL(AppendStringToString(scr, string, app->string, app->len));
  172.  
  173.   }
  174.   return(FPL_OK);
  175. }
  176.  
  177. /************************************************************************
  178.  *
  179.  * AppendStringToString()
  180.  *
  181.  * Append a generic string to a fplStr.
  182.  *
  183.  */
  184.  
  185. ReturnCode REGARGS
  186. AppendStringToString(struct Data *scr,  /* common data struct */
  187.              struct fplStr **string, /* variable to append to */
  188.              uchar *append, /* string to append */
  189.              long applen)  /* length of append string */
  190. {
  191.     long length;
  192.     long alloc;
  193.     long ln;
  194.     struct fplStr *pek;
  195.     ReturnCode ret;
  196.     void *dest;
  197.     uchar type = *string?TypeMem(*string):MALLOC_DYNAMIC;
  198.  
  199.     length=*string?(*string)->len:0;
  200.     alloc=*string?(*string)->alloc:0;
  201.  
  202.     ln = applen + length; /* total length */
  203.     if (ln>=alloc) { /* do we have that much memory allocated? */
  204.       /*
  205.        * Allocate new memory for string.
  206.        */
  207.  
  208.       GETMEM(pek, sizeof(struct fplStr)+ln+ADDSTRING_INC);
  209.       if(MALLOC_STATIC == type)
  210.         SwapMem(scr, pek, MALLOC_STATIC);
  211.  
  212.       if(*string) {
  213.         memcpy(pek, (*string), length+sizeof(struct fplStr));
  214.         FREE_KIND(*string);
  215.       } else
  216.     pek->len=0;
  217. #ifdef DEBUG
  218.       CheckMem(scr, pek);
  219. #endif
  220.       (*string)=pek;              /* the new pointer */
  221.       (*string)->alloc=ln+ADDSTRING_INC;  /* new allocated size */
  222.     }
  223.  
  224.     dest=(void *)&(*string)->string[length];
  225.  
  226.     /* no string function... only mem-versions! */
  227.     memcpy(dest, (void *)append, applen);
  228.  
  229.     (*string)->len += applen;
  230.  
  231.     (*string)->string[(*string)->len]=CHAR_ASCII_ZERO; /* zero terminate */
  232.  
  233.     return FPL_OK;
  234. }
  235.  
  236.  
  237. /************************************************************************
  238.  *
  239.  * ReturnChar()
  240.  *
  241.  * Returns the ASCII code of the character scr->text points to.
  242.  *
  243.  * Supports 100% ANSI C escape sequences.
  244.  */
  245.  
  246. ReturnCode REGARGS
  247. ReturnChar(struct Data *scr,
  248.            long *num,
  249.            uchar string) /* is this is within quotes */
  250. {
  251.   ReturnCode ret=FPL_OK;
  252.   long cont=TRUE, steps;
  253.   *num=256;
  254.   while(cont) {
  255.     cont=FALSE;
  256.     if(*scr->text==CHAR_BACKSLASH) {
  257.       steps=2;
  258.       switch(scr->text[1]) {
  259.       case CHAR_B:
  260.     *num=CHAR_BACKSPACE;
  261.     break;
  262.       case CHAR_T:
  263.     *num=CHAR_TAB;
  264.     break;
  265.       case CHAR_N:
  266.     *num=CHAR_NEWLINE;
  267.     break;
  268.       case CHAR_F:
  269.     *num=CHAR_FORMFEED;
  270.     break;
  271.       case CHAR_BACKSLASH:
  272.     *num=CHAR_BACKSLASH;
  273.     break;
  274.       case CHAR_QUOTATION_MARK:
  275.     *num=CHAR_QUOTATION_MARK;
  276.     break;
  277.       case CHAR_APOSTROPHE:
  278.     *num=CHAR_APOSTROPHE;
  279.     break;
  280.       case CHAR_A:
  281.     *num=CHAR_ALERT;
  282.     /*   ^^^^ causes warnings ('warning: \a is ANSI C "alert" character')
  283.      * on some compilers. Ignore and look happy!
  284.      */
  285.     break;
  286.       case CHAR_R:
  287.     *num=CHAR_CARRIAGE_RETURN;
  288.     break;
  289.       case CHAR_V:
  290.     *num=CHAR_VERTICAL_TAB;
  291.     break;
  292.       case CHAR_QUESTION:
  293.     *num=CHAR_QUESTION;
  294.     break;
  295.       case CHAR_X:
  296.     steps=*num=0;
  297.     for(scr->text+=2; steps++<2 && isxdigit(*scr->text); scr->text++)
  298.       *num=*num*16+ (isdigit(*scr->text)?
  299.                          *scr->text-CHAR_ZERO:
  300.                          UPPER(*scr->text)-CHAR_UPPER_A+10);
  301.         if(!steps)
  302.       return(FPLERR_SYNTAX_ERROR); /* no number followed \x sequence */
  303.     steps=0;
  304.     break;
  305.       case CHAR_ZERO:
  306.       case CHAR_ONE:
  307.       case CHAR_TWO:
  308.       case CHAR_THREE:
  309.       case CHAR_FOUR:
  310.       case CHAR_FIVE:
  311.       case CHAR_SIX:
  312.       case CHAR_SEVEN:
  313.     *num=steps=0;
  314.     for(scr->text++;steps++<3 && isodigit(*scr->text);)
  315.       *num=*num*8+ *scr->text++ - CHAR_ZERO;
  316.     steps=0;
  317.     break;
  318.       case CHAR_NEWLINE:
  319.     /* After a line continuation backslash, a newline is required!
  320.        This is made to apply to the ANSI C escape sequence standard.
  321.        (added 930113-1305 / DaSt) */
  322.     cont=TRUE;
  323.     scr->virprg++;
  324.     break;
  325.       case CHAR_ASCII_ZERO:
  326.         /* Added 950603 to make the fplConvertString() work better if the
  327.            string to convert ends with a backslash! */
  328.         cont=TRUE;
  329.         CALL(Newline(scr));
  330.         steps=0;
  331.         break;
  332.       default:
  333.     /* Any character not identified as a escape sequence character
  334.        will simply ignore the backslah character!
  335.        (added 930113-1307 / DaSt) */
  336.     *num=scr->text[1];
  337.     break;
  338.       }
  339.       scr->text+=steps;
  340.     } else if(!string && *scr->text== CHAR_NEWLINE) {
  341.       /* This won't occur if the script is preprocessed! */
  342.       cont=TRUE;
  343.       scr->text++;
  344.     } else if(*scr->text== CHAR_ASCII_ZERO) {
  345.       /* This won't occur if the script is preprocessed! */
  346.       cont=TRUE;
  347.       CALL(Newline(scr));
  348.     } else {
  349.       *num=*scr->text;
  350.       scr->text++;
  351.     }
  352.   }
  353.   return(ret);
  354. }
  355.  
  356. /**********************************************************************
  357.  *
  358.  * ReturnCode NewMember(struct Expr **);
  359.  *
  360.  * This function adds a new member in the linked list which keeps
  361.  * track on every operand and it's opertor in the expression.
  362.  *
  363.  *******/
  364.  
  365. ReturnCode REGARGS
  366. NewMember(struct Data *scr,
  367.           struct Expr **expr)
  368. {
  369.   GETMEM((*expr)->next, sizeof(struct Expr));
  370.  
  371.   (*expr)=(*expr)->next;
  372.   (*expr)->val.val=0;
  373.   (*expr)->unary=NULL;
  374.   (*expr)->operator=OP_NOTHING;
  375.   (*expr)->flags=FPL_OPERAND;
  376.   (*expr)->next=NULL;
  377.   return(FPL_OK);
  378. }
  379.  
  380.  
  381. /**********************************************************************
  382.  *
  383.  * ReturnCode Warn();
  384.  *
  385.  * This routines calls the interface function to ask for permission to
  386.  * continue the execution, even though error(s) has/have been found in
  387.  * the interpreted program.
  388.  *
  389.  ******/
  390.  
  391. ReturnCode REGARGS
  392. Warn(struct Data *scr,
  393.      ReturnCode rtrn)
  394. {
  395.   struct fplArgument *pass;
  396.   struct fplMsg *msg;
  397.   ReturnCode ret;
  398.  
  399.   GETMEM(pass, sizeof(struct fplArgument));
  400.   pass->ID=FPL_WARNING;
  401.   pass->key=scr;
  402.   pass->argc=1;
  403.   pass->argv=(void **)&rtrn; /* first ->argv member holds the error/warning number! */
  404.  
  405.   ret=InterfaceCall(scr, pass, scr->function);
  406.  
  407.   FREE(pass);
  408.   GetMessage(scr, FPLMSG_CONFIRM, &msg);
  409.   if(msg) {
  410.     if(msg->message[0]) {
  411.       rtrn=ret;
  412.       scr->prog->warnings++;
  413.     }
  414.     DeleteMessage(scr, msg);
  415.   }
  416.   return(rtrn);
  417. }
  418.  
  419.  
  420. #ifndef AMIGA /* if not using SAS/C on Amiga */
  421.  
  422. #ifdef VARARG_FUNCTIONS
  423. long fplSendTags(void *anchor, ...)
  424. {
  425.   va_list tags;
  426.   long ret;
  427.   va_start(tags, anchor); /* get parameter list */
  428.   ret = fplSend(anchor, (unsigned long *)tags);
  429.   va_end(tags)
  430.   return ret;
  431. }
  432. #else
  433. long fplSendTags(void *anchor, unsigned long tags, ...)
  434. {
  435.   return(fplSend(anchor, &tags));
  436. }
  437. #endif
  438.  
  439. #endif
  440.  
  441. /**********************************************************************
  442.  *
  443.  * fplSend()
  444.  *
  445.  * Send a message to FPL.
  446.  *
  447.  ******/
  448.  
  449. ReturnCode PREFIX fplSend(AREG(0) struct Data *scr,
  450.               AREG(1) unsigned long *tags)
  451. {
  452. #ifdef DEBUGMAIL
  453.   DebugMail(scr, MAIL_FUNCTION, 500, "fplSend");
  454. #endif
  455.   return Send(scr, tags);
  456. }
  457.  
  458. ReturnCode REGARGS Send(struct Data *scr,
  459.             unsigned long *tags)
  460. {
  461.   struct fplMsg msg;
  462.   long len=-1;
  463.   struct Program *prog;
  464.   uchar *data=NULL;
  465.   ReturnCode ret;
  466.   struct fplSymbol *symbol;
  467.   struct fplStr *string;
  468.   long mixed;
  469.   static long *resultcode=NULL;
  470.   uchar fplallocstring=FALSE;
  471.   if(!scr)
  472.     return(FPLERR_ILLEGAL_ANCHOR);
  473.  
  474.   memset(&msg, 0, sizeof(struct fplMsg));
  475.  
  476.   while(tags && *tags) {
  477.     switch(*tags++) {
  478.  
  479.     case FPLSEND_STRING:
  480.       /* FPLSEND_PROGRAMFILE is the same tag */
  481.       data=(void *)*tags;
  482.       msg.type=FPLMSG_RETURN;
  483.       msg.flags = FPLMSG_FLG_STRING;
  484.       break;
  485.  
  486.     case FPLSEND_STRLEN:
  487.       len=(long)*tags;
  488.       break;
  489.  
  490.     case FPLSEND_DONTCOPY_STRING: /* the string sent is fplAllocString()'ed */
  491.       fplallocstring=(uchar)*tags;
  492.       break;
  493.  
  494.     case FPLSEND_INT:
  495.       msg.message[0]=(void *)*tags;
  496.       msg.type=FPLMSG_RETURN;
  497.       msg.flags = FPLMSG_FLG_INT;
  498.       break;
  499.  
  500.     case FPLSEND_PROGRAM:
  501.       msg.message[0]=(void *)*tags;
  502.       msg.type=FPLMSG_PROGRAM;
  503.       break;
  504.  
  505.     case FPLSEND_CONFIRM:
  506.       msg.type=FPLMSG_CONFIRM;
  507.       msg.message[0]=(void *)*tags;
  508.       break;
  509.  
  510.     case FPLSEND_GETINTERVAL:
  511.       *(long *)*tags=(long)scr->interfunc;
  512.       break;
  513.  
  514.     case FPLSEND_GETFUNCTION:
  515.       *(long *)*tags=(long)scr->function;
  516.       break;
  517.  
  518.     case FPLSEND_GETLINE:
  519.       *(long *)*tags=scr->prg;
  520.       break;
  521.  
  522.     case FPLSEND_GETVIRFILE:
  523.       *(uchar **)*tags=scr->virfile;
  524.       break;
  525.  
  526.     case FPLSEND_GETVIRLINE:
  527.       *(long *)*tags=scr->virprg;
  528.       break;
  529.  
  530.     case FPLSEND_GETNEWLINE_HOOK:        /* OBSOLETE!!!! */
  531.       break;
  532.  
  533.     case FPLSEND_GETRESULT:
  534.       *(long *)*tags=scr->data;
  535.       break;
  536.  
  537.     case FPLSEND_GETRETURNCODE:
  538.       *(long *)*tags=scr->FPLret;
  539.       break;
  540.  
  541.     case FPLSEND_GETRETURNINT: /* new from V10 */
  542.       *(long **)*tags=scr->returnint;
  543.       break;
  544.  
  545.     case FPLSEND_GETUSERDATA:
  546.       *(long *)*tags=(long)scr->userdata;
  547.       break;
  548.  
  549.     case FPLSEND_GETCOLUMN:
  550.       if(scr->prog && scr->prog->running)
  551.     *(long *)*tags=(scr->text-(&scr->prog->program)[scr->prg-1]+1);
  552.       else if(scr->prog)
  553.     /* we cannot count on this programs presence */
  554.     *(long *)*tags=scr->prog->column;
  555.       else
  556.     *(long *)*tags=0; /* we don't know! */
  557.       break;
  558.  
  559.     case FPLSEND_GETPROGNAME:
  560.       if(scr->prog && scr->prog->name)
  561.     *(uchar **)*tags=scr->prog->name;
  562.       else /* we have no program information */
  563.     *(uchar **)*tags=FPLTEXT_UNKNOWN_PROGRAM;
  564.       break;
  565.  
  566.     case FPLSEND_GETPROG:
  567.       if(scr->prog && scr->prog->program)
  568.     *(uchar **)*tags=scr->prog->program;
  569.       else /* we have no program information */
  570.     *(uchar **)*tags=NULL;
  571.       break;
  572.  
  573.     case FPLSEND_FLUSHCACHE:
  574.       if(*tags)
  575.     FlushFree(scr);
  576.       break;
  577.  
  578.     case FPLSEND_FLUSHFILE:
  579.       if(*tags) {
  580.     prog=scr->programs;
  581.     while(prog) {
  582.       if(prog->name && !strcmp(prog->name, (uchar *)*tags))
  583.         break;
  584.       prog=prog->next;
  585.     }
  586.     if(!prog)
  587.       return(FPLERR_INTERNAL_ERROR);
  588.       } else
  589.     prog=scr->programs;
  590.       while(prog) {
  591.     if(!(prog->running)) {
  592.       /* if the program isn't running right now! */
  593.       len=prog->flags;
  594.       prog->flags&=~PR_CACHEFILE; /* switch off the cache bit now */
  595.       CALL(LeaveProgram(scr, prog));
  596.       prog->flags=len; /* restore flag bits! */
  597.     }
  598.     if(*tags)
  599.       /* only the specified */
  600.       break;
  601.     prog=prog->next;
  602.       }
  603.       break;
  604.  
  605.     case FPLSEND_FREEFILE:
  606.       prog=scr->programs;
  607.       while(prog) {
  608.     if(prog->name && !strcmp(prog->name, (uchar *)*tags))
  609.       break;
  610.     prog=prog->next;
  611.       }
  612.       if(!prog || prog->running || prog->openings)
  613.     /* if not found or if the found one is currently in use! */
  614.     return(FPLERR_ILLEGAL_PARAMETER);
  615.       {
  616.         for(mixed=0; mixed<scr->hash_size; mixed++) {
  617.           register struct Identifier *nident;
  618.           register struct Identifier *ident = scr->hash[mixed];
  619.           while(ident) {
  620.             nident=ident->next;
  621.             if(!strcmp(ident->file, (uchar *)*tags))
  622.               DelIdentifier(scr, NULL, ident);
  623.             ident=nident;
  624.           }
  625.         }
  626.       }
  627.       DelProgram(scr, prog);
  628.       break;
  629.  
  630.     case FPLSEND_STEP:
  631.       if(*tags>0) {
  632.     while((*tags)--) {
  633.       if(!*scr->text)
  634.         CALL(Newline(scr));
  635.       scr->text++;
  636.     }
  637.       } else if((signed int)(*tags)<0) {
  638.     while((*tags)++) {
  639.       if( (scr->text-(&scr->prog->program)[scr->prg-1])>=0)
  640.         scr->text--;
  641.       else
  642.         if(scr->prg>1)
  643.           scr->text=(&scr->prog->program)[--scr->prg-1];
  644.         else
  645.           return(FPLERR_UNEXPECTED_END);
  646.     }
  647.       }
  648.       break;
  649.     case FPLSEND_GETSYMBOL_FUNCTIONS:
  650.       CALL(GetSymbols(scr, FPL_EXTERNAL_FUNCTION|FPL_INSIDE_FUNCTION,
  651.               FPL_EXPORT_SYMBOL,
  652.               (struct fplSymbol **)*tags));
  653.       break;
  654.     case FPLSEND_GETSYMBOL_MYFUNCTIONS:
  655.       CALL(GetSymbols(scr, FPL_EXTERNAL_FUNCTION, FPL_FUNCTION,
  656.               (struct fplSymbol **)*tags));
  657.       break;
  658.     case FPLSEND_GETSYMBOL_FPLFUNCTIONS:
  659.       CALL(GetSymbols(scr, FPL_EXPORT_SYMBOL, FPL_INSIDE_FUNCTION,
  660.               (struct fplSymbol **)*tags));
  661.       break;
  662.     case FPLSEND_GETSYMBOL_VARIABLES:
  663.       CALL(GetSymbols(scr, FPL_EXPORT_SYMBOL, FPL_VARIABLE,
  664.               (struct fplSymbol **)*tags));
  665.       break;
  666.     case FPLSEND_GETSYMBOL_ALLVARIABLES:
  667.       CALL(GetSymbols(scr, ~0, FPL_VARIABLE, (struct fplSymbol **)*tags));
  668.       break;
  669.  
  670.     case FPLSEND_GETSYMBOL_ALLFUNCTIONS:
  671.       CALL(GetSymbols(scr, ~0, FPL_FUNCTION, (struct fplSymbol **)*tags));
  672.       break;
  673.  
  674.     case FPLSEND_GETSYMBOL_CACHEDFILES:
  675.       prog=scr->programs;
  676.       mixed=0;
  677.       while(prog) {
  678.     if(prog->flags&PR_CACHEFILE)
  679.       mixed++;
  680.     prog=prog->next;
  681.       }
  682.  
  683.       GETMEM(symbol, sizeof(struct fplSymbol));
  684.       symbol->num=mixed;
  685.       GETMEM(symbol->array, mixed*sizeof(uchar *));
  686.  
  687.       mixed=0;
  688.       prog=scr->programs;
  689.       while(prog) {
  690.     if(prog->flags&PR_CACHEFILE)
  691.       symbol->array[mixed++]=prog->name;
  692.     prog=prog->next;
  693.       }
  694.       *(struct fplSymbol **)*tags=symbol;
  695.  
  696. #ifdef DEBUG
  697.       CheckMem(scr, symbol);
  698.       CheckMem(scr, symbol->array);
  699. #endif
  700.  
  701.       break;
  702.  
  703.       /* ----------------  new from V10: --------------------- */
  704.  
  705.     case FPLSEND_RESULT:
  706.       resultcode = (long *)(*tags); /* long to store result in! */
  707.       break;
  708.  
  709.     case FPLSEND_IS_FILE_CACHED:
  710.       if(resultcode) {
  711.         prog=scr->programs;
  712.         while(prog) {
  713.           if(prog->name && !strcmp(prog->name, (uchar *)(*tags))) {
  714.             *resultcode = TRUE;
  715.             break;
  716.           }
  717.           prog=prog->next;
  718.         }
  719.         *resultcode = FALSE;
  720.       }
  721.       break;
  722.     
  723.       /* --------------------------------------------------- */
  724.  
  725.     case FPLSEND_GETSYMBOL_FREE:
  726. #ifdef DEBUG
  727.       CheckMem(scr, (void *)(*tags));
  728.       CheckMem(scr, ((struct fplSymbol *)*tags)->array);
  729. #endif
  730.       FREE(((struct fplSymbol *)*tags)->array);
  731.       FREE(*tags);
  732.       break;
  733.  
  734. #if defined(AMIGA) && defined(SHARED)
  735.     case FPLSEND_GETSTACKSIZE:
  736.       *(long *)*tags=scr->stack_size;
  737.       break;
  738.     case FPLSEND_GETSTACKUSED:
  739.       *(long *)*tags=GetStackUsed(scr);
  740.       break;
  741. #endif
  742.  
  743.     case FPLSEND_SETPROGNAME:
  744.       if(scr->prog) {
  745.     if(scr->prog->name)
  746.       FREEA(scr->prog->name);
  747.     STRDUPA(scr->prog->name, *tags);
  748.       }
  749.       break;
  750.  
  751.     case FPLSEND_SETFILENAMEGET:
  752.       if(scr->prog) {
  753.     if(*tags)
  754.       scr->prog->flags|=PR_FILENAMEFLUSH;
  755.     else
  756.       scr->prog->flags&=~PR_FILENAMEFLUSH;
  757.       }
  758.       break;
  759.     case FPLSEND_GETVERSION:
  760.       *(long *)(*tags) = FPL_VERSION; /* as defined in FPL.h */
  761.       break;
  762.     case FPLSEND_GETREVISION:
  763.       *(long *)(*tags) = FPL_REVISION; /* as defined in FPL.h */
  764.       break;
  765.     }
  766.     tags++;
  767.   }
  768.   if(!msg.type)
  769.     /*
  770.      * There is no message to send. Everything we had to do is done!
  771.      */
  772.     return(FPL_OK);
  773.  
  774.   if(msg.type==FPLMSG_RETURN && (msg.flags == FPLMSG_FLG_STRING)) {
  775.     if(len<0)
  776.       if(data)
  777.     len=strlen(data);
  778.     if(!len || !data)
  779.       /* this really is a zero length string! */
  780.       msg.message[0]=NULL;
  781.     else {
  782.       if(!fplallocstring) {
  783.         /* we have to duplicate the data */
  784.         GETMEM(msg.message[0], len+sizeof(struct fplStr));
  785.         string=msg.message[0];
  786.         string->len=len;
  787.         string->alloc=len;
  788.         memcpy(string->string, data, len); /* copy string! */
  789.         string->string[string->len]=CHAR_ASCII_ZERO; /* zero terminate */
  790.       } else {
  791.         /* the data was sent as fplAllocString() data! */
  792.         string= (struct fplStr *)(data - offsetof(struct fplStr, string));
  793.         string->len=len;
  794.         string->string[string->len]=CHAR_ASCII_ZERO; /* zero terminate */
  795.         SwapMem(scr, string, MALLOC_DYNAMIC); /* convert */
  796.         msg.message[0]=string;
  797.       }
  798.     }
  799.   }
  800.   CALL(SendMessage(scr, &msg));
  801.   return(ret);
  802. }
  803.  
  804.  
  805. /*********************************************************************
  806.  *
  807.  * fplConvertString()
  808.  *
  809.  * Returns the number of characters converted from the FPL format
  810.  * string to the binary sting stored in a buffer.
  811.  *
  812.  * The output string always get zero terminated!
  813.  *
  814.  *****/
  815.  
  816. long PREFIX
  817. fplConvertString(AREG(0) struct Data *scr,
  818.                  AREG(1) uchar *string,
  819.                  AREG(2) uchar *buffer)
  820. {
  821.   long prg=scr->prg;
  822.   uchar *text=scr->text;
  823.   long line;
  824.   uchar *base;
  825.   long a;
  826.   long number=0;
  827.  
  828. #ifdef DEBUGMAIL
  829.   DebugMail(scr, MAIL_FUNCTION, 500, "fplConvertString");
  830. #endif
  831.  
  832.   if(!scr->prog) {
  833.     /*
  834.      * There is no program at the moment!
  835.      * create a pseudo program for now!
  836.      */
  837.     scr->prog=(struct Program *)MALLOC(sizeof(struct Program));
  838.     if(!scr->prog)
  839.       return(0); /* no characters in output! */
  840.     scr->prog->flags|=PR_TEMPORARY;
  841.   }
  842.   
  843.   base=scr->prog->name;
  844.   line=scr->prog->lines;
  845. /*
  846.   if(*string==CHAR_QUOTATION_MARK)
  847.     string++;
  848. */
  849.  
  850.   scr->prg=1;
  851.   scr->text=string;
  852.   scr->prog->lines=1;
  853.   scr->prog->name=NULL; /* we have no file ID yet! */
  854.  
  855.   while(/* *scr->text!=CHAR_QUOTATION_MARK && */
  856.     !ReturnChar(scr, &a, TRUE)) { /* returns non-zero when an ascii zero is
  857.                          found! */
  858.     *buffer++=a;
  859.     number++;
  860.   }
  861.   
  862.   *buffer=CHAR_ASCII_ZERO;
  863.  
  864.   scr->prg=prg;
  865.   scr->text=text;
  866.   scr->prog->lines=line;
  867.   scr->prog->name=base;
  868.   
  869.   if(scr->prog->flags&PR_TEMPORARY) {
  870.     FREE(scr->prog);
  871.     scr->prog=NULL;
  872.   }
  873.  
  874.   return(number);
  875. }
  876.  
  877. /**********************************************************************
  878.  *
  879.  * GetSymbols();
  880.  *
  881.  * Allocates a structure and data, which is a list of name pointers
  882.  * that match the flag parameter.
  883.  *
  884.  *******/
  885.  
  886. static ReturnCode REGARGS
  887. GetSymbols(struct Data *scr,
  888.            long flag1,
  889.            long flag2,
  890.            struct fplSymbol **get)
  891. {
  892.   long i;
  893.   long num;
  894.   struct Identifier *ident;
  895.   struct fplSymbol *symbol;
  896.  
  897.   for(i=num=0; i<scr->hash_size; i++) {
  898.     ident=scr->hash[i];
  899.     while(ident) {
  900.       if(ident->flags&flag1 && ident->flags&flag2)
  901.     num++;
  902.       ident=ident->next;
  903.     }
  904.   }
  905.  
  906.   GETMEM(symbol, sizeof(struct fplSymbol));
  907.   symbol->num=num;
  908.  
  909.   GETMEM(symbol->array, sizeof(uchar *)*symbol->num);
  910.  
  911.   for(i=num=0; i<scr->hash_size; i++) {
  912.     ident=scr->hash[i];
  913.     while(ident) {
  914.       if(ident->flags&flag1 && ident->flags&flag2)
  915.     symbol->array[num++]=ident->name;
  916.       ident=ident->next;
  917.     }
  918.   }
  919.   *get=symbol;
  920.  
  921. #ifdef DEBUG
  922.   CheckMem(scr, symbol->array);
  923. #endif
  924.   return(FPL_OK);
  925. }
  926.  
  927.  
  928. /**********************************************************************
  929.  *
  930.  * SendMessage();
  931.  *
  932.  * Add a member to the message queue. Allocate a new struct and copy the
  933.  * data of from second parameter message pointer.
  934.  *
  935.  ******/
  936.  
  937. static ReturnCode INLINE
  938. SendMessage(struct Data *scr,
  939.             struct fplMsg *msg)
  940. {
  941.   struct fplMsg *NewMsg, *ptr;
  942.  
  943.   GETMEM(NewMsg, sizeof(struct fplMsg));
  944.  
  945.   *NewMsg=*msg; /* copy all data from source */
  946.  
  947.   /* Queue the message: */
  948.   if(ptr=scr->msg)
  949.     ptr->prev=NewMsg; /* this message becomes the previous for this */
  950.  
  951.   scr->msg=NewMsg;
  952.   NewMsg->next=ptr;
  953.   NewMsg->prev=NULL; /* no previous, this is first! */
  954.  
  955.   return(FPL_OK);
  956. }
  957.  
  958. /**********************************************************************
  959.  *
  960.  * DeleteMessage();
  961.  *
  962.  * Deletes specified or current message (NULL).
  963.  *
  964.  *****/
  965.  
  966. ReturnCode REGARGS
  967. DeleteMessage(struct Data *scr,
  968.               struct fplMsg *msg)
  969. {
  970.   struct fplMsg *ptr=scr->msg;
  971.   if(msg) 
  972.     ptr=msg;
  973.   if(ptr) {
  974.     if(ptr->next)
  975.       ptr->next->prev=ptr->prev; /* redirect next message's prev pointer */
  976.     else if(!ptr->prev) /* is this the only message? */
  977.       scr->msg=NULL;
  978.     if(ptr->prev)
  979.       ptr->prev->next=ptr->next; /* redirect previous message's next pointer */
  980.     FREE(ptr);  /* free message */
  981.   }
  982.   return(FPL_OK);
  983. }
  984.  
  985. /**********************************************************************
  986.  *
  987.  * GetMessage()
  988.  *
  989.  * Returns the first message of the requested type in the pointer
  990.  * in the third argument!
  991.  *
  992.  ****/
  993.  
  994. ReturnCode REGARGS
  995. GetMessage(struct Data *scr,
  996.            uchar type,
  997.            struct fplMsg **get)
  998. {
  999.   struct fplMsg *msg=scr->msg;
  1000.   while(*get=msg) {
  1001.     if(msg->type==type)
  1002.       break;
  1003.     msg=msg->next;
  1004.   }
  1005.   return(FPL_OK);
  1006. }
  1007.  
  1008. /**********************************************************************
  1009.  *
  1010.  * GetProgram();
  1011.  *
  1012.  * Whenever we want to access a program in the program list, we do it
  1013.  * using this function. This enables heavy program swapping capabilities.
  1014.  * Programs that are not being used can be flushed from memory and brought
  1015.  * back whenever we need it!
  1016.  *
  1017.  ******/
  1018.  
  1019. ReturnCode REGARGS
  1020. GetProgram(struct Data *scr,
  1021.            struct Program *prog)
  1022. {
  1023.   struct fplArgument *arg;
  1024.   ReturnCode ret;
  1025.   struct fplMsg *msg;
  1026.   struct fplStr *string;
  1027.   if(!prog->program) {
  1028.     /*
  1029.      * The program is not currently in memory. Get it!
  1030.      */
  1031.     
  1032.     if(prog->flags&PR_FILENAMEFLUSH) {
  1033.       /*
  1034.        * We know that the program is simply to load from the file the program
  1035.        * name specifies.
  1036.        */
  1037.       CALL(ReadFile(scr, prog->name, prog));
  1038.     } else {
  1039.       /*
  1040.        * We must ask user for information!
  1041.        */
  1042.       
  1043.       GETMEM(arg, sizeof(struct fplArgument));
  1044.       arg->ID=FPL_FILE_REQUEST;
  1045.       arg->key=(void *)scr;
  1046.       arg->argv=(void **)&prog->name;
  1047.       arg->argc=1;
  1048.       ret=InterfaceCall(scr, arg, scr->function);
  1049.       FREE(arg);
  1050.       if(ret)
  1051.         return ret;
  1052.       
  1053.       GetMessage(scr, FPLMSG_PROGRAM, &msg);
  1054.       if(!msg) {
  1055.     GetMessage(scr, FPLMSG_RETURN, &msg);
  1056.     if(!msg || ((msg->flags&FPLMSG_FLG_BITS) != FPLMSG_FLG_STRING))
  1057.       /*
  1058.        * No kind of proper answer could be found!
  1059.        * Dead end failure!
  1060.        */      
  1061.       return FPLERR_INTERNAL_ERROR;
  1062.     
  1063.     string=(struct fplStr *)msg->message[0];
  1064.     ret = ReadFile(scr, string->string, prog);
  1065.     FREE(msg->message[0]); /* we don't need this anymore! */
  1066.     if(ret)
  1067.       return ret;
  1068.       } else {
  1069.     /*
  1070.      * User supplied us with a memory pointer to the program again!
  1071.      */
  1072.     prog->program= (uchar *)msg->message[0];
  1073.     prog->flags|=PR_USERSUPPLIED;
  1074.       }
  1075.       DeleteMessage(scr, msg);
  1076.     }
  1077.   } /* else
  1078.        we already have it loaded! */
  1079.   prog->running++;
  1080.   return(FPL_OK);
  1081. }
  1082.  
  1083.  
  1084. /**********************************************************************
  1085.  *
  1086.  * LeaveProgram();
  1087.  *
  1088.  * If we leave one program, call this. If any flush is to be done, this
  1089.  * will perform that!
  1090.  *
  1091.  ******/
  1092.  
  1093. ReturnCode REGARGS
  1094. LeaveProgram(struct Data *scr,
  1095.              struct Program *prog)
  1096. {
  1097.   struct fplArgument *arg;
  1098.   ReturnCode ret;
  1099.   struct fplMsg *msg;
  1100.   prog->running--;
  1101.   if(prog->program && !prog->running && prog->flags&PR_FLUSH_NOT_IN_USE) {
  1102.     /*
  1103.      * The program is there and no one is using it!
  1104.      * flush it if we want to!
  1105.      */
  1106.  
  1107.     if(prog->flags&PR_USERSUPPLIED) {
  1108.       /*
  1109.        * This program is supplied by the external program. We cannot
  1110.        * free the memory, only tell our father that freeing is OK...
  1111.        */
  1112.       GETMEM(arg, sizeof(struct fplArgument));
  1113.       arg->ID=FPL_FLUSH_FILE;
  1114.       arg->key=(void *)scr;
  1115.       arg->argv=(void **)&prog->name;
  1116.       arg->argc=1;
  1117.       CALL(InterfaceCall(scr, arg, scr->function));
  1118.       FREE(arg);
  1119.       GetMessage(scr, FPLMSG_CONFIRM, &msg);
  1120.       /*
  1121.        * We require a {FPLSEND_CONFIRM, TRUE} message from the user before we
  1122.        * flush the user supplied function! Simply ignore implementing any
  1123.        * answer to this message if we never want to flush user supplied
  1124.        * functions.
  1125.        */
  1126.       if(msg && msg->message[0])
  1127.     /* If we got a "OK" message! */
  1128.     prog->program=NULL;
  1129.       if(msg)
  1130.     DeleteMessage(scr, msg);
  1131.     } else {
  1132.       /*
  1133.        * The memory occupied by this program is our business.
  1134.        * Swap the memory first to be sure we know the kind of it!
  1135.        */
  1136.       SwapMem(scr, prog->program, MALLOC_DYNAMIC);
  1137.       FREE(prog->program);
  1138.       prog->program=NULL; /* to visualize the clearing of this program! */
  1139.     }
  1140.   }
  1141.   return(FPL_OK);
  1142. }
  1143.  
  1144. static long INLINE
  1145. Exists(struct Data *scr,
  1146.        uchar *name,
  1147.        long check)
  1148. {
  1149.   ReturnCode ret;
  1150.   struct Identifier *ident;
  1151.   GetIdentifier(scr, name, &ident);
  1152.   if(check && ident) {
  1153.     switch(tolower(check)) {
  1154.     case EXISTS_FUNCTION:
  1155.       ret = ident->flags&FPL_FUNCTION;
  1156.       break;
  1157.     case EXISTS_INTEGER:
  1158.       ret = ident->flags&FPL_INT_VARIABLE;
  1159.       break;
  1160.     case EXISTS_STRING:
  1161.       ret = ident->flags&FPL_STRING_VARIABLE;
  1162.       break;
  1163.     case EXISTS_VARIABLE:
  1164.       ret = ident->flags&FPL_VARIABLE;
  1165.       break;
  1166.     }
  1167.   }
  1168.   else
  1169.     ret = ident?TRUE:FALSE;
  1170.   return ret;
  1171. }
  1172.  
  1173. /**********************************************************************
  1174.  *
  1175.  * int functions(struct fplArgument *);
  1176.  *
  1177.  * This function handles the internal functions. *EXACTLY* the same way
  1178.  * external processes handles their functions!!! :-)
  1179.  *
  1180.  *****/
  1181.  
  1182. ReturnCode REGARGS
  1183. functions(struct fplArgument *arg)
  1184. {
  1185.   struct Expr *val;
  1186.   unsigned long inttags[]={FPLSEND_INT, 0, FPLSEND_DONE};
  1187.   unsigned long strtags[]={FPLSEND_STRING, 0, FPLSEND_STRLEN, 0, FPLSEND_DONE};
  1188.   long base;
  1189.   ReturnCode ret;
  1190.   struct Data *scr=(struct Data *)arg->key;
  1191.   struct fplStr *string;
  1192.   long prg;
  1193.   long line;
  1194.   long virprg;
  1195.   uchar *virfile;
  1196.   uchar *text;
  1197. /*  uchar *file; */
  1198.   long len;        /* length of the string */
  1199.   register long col;    /* the column parameter */
  1200.   switch(arg->ID) {
  1201.     
  1202.   case FNC_ABS:
  1203.     inttags[1]= ABS((long)arg->argv[0]);
  1204.     CALL(Send(arg->key, inttags));
  1205.     break;
  1206.  
  1207.   case FNC_ITOC:
  1208.     prg=(long)arg->argv[0]&255;
  1209.     text=(uchar *)&line; /* we just need 2 bytes to play with in peace! */
  1210.     text[1]='\0';
  1211.     text[0]=prg;
  1212.     strtags[1]=(long)text;
  1213.     strtags[3]=1;
  1214.     CALL(Send(scr, strtags));
  1215.     break;
  1216.     
  1217.   case FNC_JOINSTR:
  1218.     string=NULL;
  1219.     for(prg=0; prg<arg->argc; prg++) {
  1220.       CALL(StrAssign((struct fplStr *) ((uchar *)arg->argv[prg]-
  1221.                     offsetof(struct fplStr, string)),
  1222.              scr, &string, TRUE));
  1223.     }
  1224.     if(string) {
  1225.       strtags[1]=(unsigned long)string->string;
  1226.       strtags[3]=string->len;
  1227.       CALL(Send(scr, strtags));
  1228.       FREE(string);
  1229.     }
  1230.     break;
  1231.  
  1232.   case FNC_ITOA:
  1233.   case FNC_LTOSTR:
  1234.     base=(arg->argc<2?10:(long)arg->argv[1]);
  1235.     CALL(Ltostr(scr, &string, base, (long)arg->argv[0]));
  1236.     strtags[1]=(unsigned long)string->string;
  1237.     strtags[3]=string->len;
  1238.     CALL(Send(scr, strtags));
  1239.     FREE(string);
  1240.     break;
  1241.     
  1242.   case FNC_ATOI:
  1243.   case FNC_STRTOL:
  1244.     base=(arg->argc<2?10:(long)arg->argv[1]);
  1245.     inttags[1]= Strtol((uchar *)arg->argv[0], base, &text);
  1246.     CALL(Send(scr, inttags));
  1247.     break;
  1248.     
  1249.   case FNC_EVAL:
  1250.     prg=scr->prg;
  1251.     text=scr->text;
  1252.     line=scr->prog->lines;
  1253.     virprg=scr->virprg;
  1254.     virfile=scr->virfile;
  1255.  
  1256.     scr->virprg=1;
  1257.     scr->virfile=NULL;
  1258.     scr->text=(uchar *)arg->argv[0];
  1259.     scr->prg=scr->prog->lines=1;
  1260.  
  1261.     GETMEM(val, sizeof(struct Expr));
  1262.     CALL(Expression(val, scr, CON_GROUNDLVL|CON_END|CON_NUM, NULL));
  1263.     inttags[1]=val->val.val;
  1264.     FREE(val);
  1265.  
  1266.     scr->prg=prg;
  1267.     scr->text=text;
  1268.     scr->prog->lines=line;
  1269.     scr->virprg=virprg;
  1270.     scr->virfile=virfile;
  1271.     
  1272.     CALL(Send(scr, inttags));
  1273.     break;
  1274.     
  1275.   case FNC_INTERPRET:
  1276.     prg=scr->prg;
  1277.     text=scr->text;
  1278.     line=scr->prog->lines;
  1279. /*    file=scr->prog->name; */
  1280.     virprg=scr->virprg;
  1281.     virfile=scr->virfile;
  1282.     scr->virprg=1;
  1283.     scr->virfile=NULL;
  1284.     scr->interpret=NULL; /* nothing recursive here, no no! */
  1285.     scr->prg=1;
  1286.     scr->text=(uchar *)arg->argv[0];
  1287.     scr->prog->lines=1;
  1288. /*    scr->prog->name=NULL; */ /* we have no file name! */
  1289.  
  1290.     GETMEM(val, sizeof(struct Expr));
  1291.     ret=Script(scr, val, SCR_NORMAL, NULL);
  1292.     inttags[1]=val->val.val;
  1293.     FREE(val);
  1294.  
  1295.     if(ret) {
  1296.       /*
  1297.        * Check if the error occurred somewhere in the real program
  1298.        * or if it was within the argument. If within argument, we
  1299.        * set back the previous program pointer, otherwise not.
  1300.        */
  1301.       for(base=0;base<line;base++)
  1302.     if(scr->text>(&scr->prog->program)[base] &&
  1303.        scr->text<((&scr->prog->program)[base]+
  1304.               strlen((&scr->prog->program)[base])))
  1305.       break;
  1306.       if(base==line) {
  1307.     scr->prg=prg;
  1308.     scr->text=text;
  1309.     scr->prog->lines=line;
  1310. /*    scr->prog->name=file; */
  1311.       }
  1312.       return(ret);
  1313.     }
  1314.     scr->prg=prg;
  1315.     scr->text=text;
  1316.     scr->prog->lines=line;
  1317. /*    scr->prog->name=file; */
  1318.     scr->virprg=virprg;
  1319.     scr->virfile=virfile;
  1320.     CALL(Send(arg->key, inttags));
  1321.     break;
  1322.     
  1323.   case FNC_RENAME:
  1324.     {
  1325.       struct Identifier *ident;
  1326.       GetIdentifier(scr, (uchar *)arg->argv[0], &ident);
  1327.       if(!ident || ident->flags&FPL_KEYWORD) {
  1328.         /* not found or found keyword, fail! */
  1329.         inttags[1] = FPLERR_IDENTIFIER_NOT_FOUND;
  1330.       }
  1331.       else {
  1332.         if(((char *)arg->argv[1])[0])
  1333.           inttags[1] = RenameIdentifier(scr, ident, arg->argv[1]);
  1334.         else
  1335.           inttags[1] = DelIdentifier(scr, NULL, ident);
  1336.       }
  1337.       CALL(Send(arg->key, inttags));
  1338.     }    
  1339.   break;
  1340.  
  1341.   case FNC_STRCMP:
  1342.   case FNC_STRICMP:
  1343.     /*
  1344.      * strcmp() with strings that can include a zero byte must use
  1345.      * memcmp(), but that also takes a third length argument which
  1346.      * must never be larger than the smallest of the two compared
  1347.      * strings!
  1348.      */
  1349.     line = MIN(FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)); /* len */
  1350.  
  1351.     if(FNC_STRCMP == arg->ID)
  1352.       base = memcmp(arg->argv[0], arg->argv[1], line);
  1353.     else
  1354.       base = my_memicmp(arg->argv[0], arg->argv[1], line);
  1355.  
  1356.     if(!base && FPL_STRING_LENGTH(arg, 0) != FPL_STRING_LENGTH(arg, 1)) {
  1357.       /* similar strings after 'line' characters */
  1358.  
  1359.       /*
  1360.        * The strings are of different length.
  1361.        */
  1362.  
  1363.       base = ((uchar *)arg->argv[0])[line] -
  1364.     (FPL_STRING_LENGTH(arg, 1)>line?
  1365.      ((uchar *)arg->argv[1])[line] : 0 );
  1366.       
  1367.       if(!base) {
  1368.     /* only possible since FPL strings can hold zeroes! */
  1369.     base = 256; /* not possible in regular C */
  1370.       }
  1371.     }
  1372.     inttags[1]=base;
  1373.     CALL(Send(scr, inttags));
  1374.     break;
  1375.     
  1376.   case FNC_SUBSTR:
  1377.     len=FPL_STRING_LENGTH(arg, 0);
  1378.     col=(long)arg->argv[1];
  1379.     if(col>len || col<0) {
  1380.       ;                /* we can't get any string! */
  1381.     } else {
  1382.       len-=col;            /* Maximum length we can get */
  1383.       strtags[3]=((long)arg->argv[2]>len?len:(long)arg->argv[2]); /* strlen */
  1384.       strtags[1]=(long) arg->argv[0]+col; /* return string from here */
  1385.       CALL(Send(scr, strtags));
  1386.     }
  1387.     break;
  1388.     
  1389.   case FNC_STRLEN:
  1390.     inttags[1]=FPL_STRING_LENGTH(arg, 0);
  1391.     CALL(Send(scr, inttags));
  1392.     break;
  1393.  
  1394.   case FNC_STRNCMP:
  1395.   case FNC_STRNICMP:
  1396.     /*
  1397.      * strncmp() with strings that can include a zero byte must use
  1398.      * memcmp(), that also takes a third length argument which
  1399.      * must never be larger than the smallest of the two compared
  1400.      * strings or the number specified!
  1401.      */
  1402.     if(FNC_STRNCMP == arg->ID) {
  1403.       inttags[1]=
  1404.     memcmp(arg->argv[0], arg->argv[1],
  1405.            MIN3((long)arg->argv[2],
  1406.             FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)));
  1407.     }
  1408.     else {
  1409.       inttags[1]=
  1410.     my_memicmp(arg->argv[0], arg->argv[1],
  1411.         MIN3((long)arg->argv[2],
  1412.              FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)));
  1413.     }
  1414.     CALL(Send(scr, inttags));
  1415.     break;
  1416.     
  1417.   case FNC_STRSTR:
  1418.   case FNC_STRISTR:
  1419.     /*
  1420.      * strstr() should compare two memory regions, like a memmem()!
  1421.      * Code an own!
  1422.      */
  1423.     base = FPL_STRLEN(arg->argv[0]);
  1424.     line = FPL_STRLEN(arg->argv[1]);
  1425.     text = (uchar *)arg->argv[0];
  1426.  
  1427.     /*
  1428.      * Addition from FPL version 9:
  1429.      * starting search column in third parameter!
  1430.      */
  1431.     if(arg->argc>2) {
  1432.       if((int)arg->argv[2] < base) {
  1433.     text+=(int)arg->argv[2];
  1434.     base-=(int)arg->argv[2];
  1435.       }
  1436.       else {
  1437.     /* tried to start searching outside the string! */
  1438.  
  1439.     line = 1; /* to make a not-found return code */
  1440.     base = 0;
  1441.       }
  1442.     }
  1443.     
  1444.     if(line && base) {
  1445.       if(FNC_STRSTR == arg->ID) {
  1446.     /* Case sensitive */
  1447.     while(base-->=line) {
  1448.       if(!memcmp(text, (uchar *)arg->argv[1], line)) {
  1449.         line=0;
  1450.         break;
  1451.       }
  1452.       text++;
  1453.     }
  1454.       }
  1455.       else {
  1456.     /* Case insensitive */
  1457.     while(base-->=line) {
  1458.       if(!my_memicmp(text, (uchar *)arg->argv[1], line)) {
  1459.         line=0;
  1460.         break;
  1461.       }
  1462.       text++;
  1463.     }
  1464.       }
  1465.     }
  1466.     inttags[1]=line?-1:text-(uchar *)arg->argv[0];
  1467.  
  1468.     /* OLD ONE:
  1469.     text=(uchar *)strstr((uchar *)arg->argv[0], (uchar *)arg->argv[1]);
  1470.     inttags[1]=text?text-(uchar *)arg->argv[0]:-1;
  1471.     */
  1472.     CALL(Send(scr, inttags));
  1473.     break;
  1474.  
  1475.   case FNC_SPRINTF:
  1476.     {
  1477.         static unsigned long tags[]={
  1478.             FPLSEND_STRING, 0,
  1479.             FPLSEND_STRLEN, 0,
  1480.             FPLSEND_DONTCOPY_STRING, TRUE,
  1481.             FPLSEND_DONE
  1482.         };
  1483.         string = NULL;
  1484.         CALL(Sprintf(scr, &string, arg->argv[0], arg->argv, arg->format,
  1485.              arg->argc));
  1486.         if(string) {
  1487.           tags[1] = (long)string->string;
  1488.           tags[3] = string->len;
  1489.           CALL(Send(scr, tags));
  1490.         }
  1491.     }
  1492.     break;
  1493.  
  1494.   case FNC_SSCANF:
  1495.     {
  1496.       inttags[1] =
  1497.       Sscanf(scr,
  1498.          (uchar *)arg->argv[0],
  1499.          (uchar *)arg->argv[1],
  1500.          arg->argc,
  1501.          arg->argv,
  1502.          arg->format);
  1503.       CALL(Send(scr, inttags));
  1504.     }
  1505.     break;
  1506.   case FNC_EXISTS:
  1507.     inttags[1] =  Exists(scr,
  1508.              (uchar *)arg->argv[0],
  1509.              arg->argc>1?(long)arg->argv[1]:0);
  1510.     CALL( Send(scr, inttags) );
  1511.     break;
  1512.  
  1513. #if defined(AMIGA)
  1514.   case FNC_OPENLIB:
  1515.     CALL(OpenLib(scr,
  1516.                  (uchar *)arg->argv[0], /* name */
  1517.                  (long)arg->argv[1],   /* version */
  1518.                  (long *)&inttags[1],  /* funclib result */
  1519.                  0));                  /* normal 'soft' open */
  1520.     CALL(Send(scr, inttags));
  1521.     break;
  1522.  
  1523.   case FNC_CLOSELIB:
  1524.     CALL(CloseLib(scr,
  1525.                   (uchar *)arg->argv[0],  /* name */
  1526.                   0,                     /* 'soft' close */
  1527.                   (long *)&inttags[1])); /* funclib result */
  1528.     CALL(Send(scr, inttags));
  1529.     break;
  1530.  
  1531.   case FNC_DEBUG:
  1532.     if(arg->argc) {
  1533.       if(!(int)arg->argv[0]) {
  1534.         scr->flags&=~FPLDATA_DEBUG_MODE; /* switch off debug mode */
  1535. #ifdef DEBUGMAIL
  1536.         DebugMail(scr, MAIL_EXIT, 500, NULL);
  1537. #endif
  1538.       }
  1539.       else {
  1540.         scr->flags|=FPLDATA_DEBUG_MODE;  /* switch on debug mode */
  1541. #ifdef DEBUGMAIL
  1542.         DebugMail(scr, MAIL_START, 500, NULL);
  1543. #endif
  1544.       }
  1545.     }
  1546.     else {
  1547.        inttags[1]=scr->flags&FPLDATA_DEBUG_MODE?1:0; /* return status */
  1548.        CALL(Send(scr, inttags));
  1549.     }
  1550.     break;
  1551. #endif
  1552.   }
  1553.   return(FPL_OK);
  1554. }
  1555.  
  1556. #if defined(AMIGA)
  1557. ReturnCode REGARGS
  1558. OpenLib(struct Data *scr,
  1559.         uchar *lib,        /* funclib name */
  1560.         long version,     /* funclib version */
  1561.         long *retvalue,   /* funclib return code */
  1562.         uchar flags)
  1563. {
  1564.    struct MyLibrary *library;
  1565.    struct Library *DOSBase;
  1566.    BPTR seglist;
  1567.    uchar *command;
  1568.    uchar *cmd;
  1569.    struct FuncList *namelist=scr->funclibs;
  1570.    uchar *name;
  1571.    ReturnCode ret;
  1572.    struct fplStr *string;
  1573.  
  1574.    struct ExecBase *SysBase = *(struct ExecBase **)4;
  1575.  
  1576.    library = (struct MyLibrary *)getreg(REG_A6);
  1577.    DOSBase = library->ml_DosBase;
  1578.  
  1579.    GETMEM(command, FPLLIB_MAXSPACE);
  1580.  
  1581.    while(namelist) {
  1582.      if(!strcmp(namelist->name, lib)) {
  1583.        namelist->opens++;
  1584.        return FPL_OK; /* this funclib is already opened */
  1585.      }
  1586.      namelist = namelist->next;
  1587.    }
  1588.  
  1589.    cmd = command;
  1590.    strcpy(command, FPLLIB_SOURCE);
  1591.    strcpy(command+strlen(FPLLIB_SOURCE), lib);
  1592.    seglist = LoadSeg(command); /* load the command! */
  1593.    if(seglist) {
  1594.      strcpy(command, FPLLIB_OPENCMD);
  1595.      command += strlen(FPLLIB_OPENCMD);
  1596.  
  1597.      CALL(Ltostr(scr, &string, 10, (long)scr));
  1598.      strcpy(command, string->string);
  1599.      command[string->len]= ' '; /* pad with a single space */
  1600.      command+=string->len+1;
  1601.      FREE(string);
  1602.  
  1603.      CALL(Ltostr(scr, &string, 10, version));
  1604.      strcpy(command, string->string);
  1605.      command[string->len]= '\n';   /* add newline */
  1606.      command[string->len+1]= '\0'; /* zero terminate */
  1607.      FREE(string);
  1608.  
  1609.      if(SysBase->SoftVer<36) {
  1610.        /* V33 solution! */
  1611.        uchar *segment = BADDR(seglist);
  1612.        int (*func)();
  1613. #pragma msg 147 ignore
  1614.        func = segment + 4; /* generates warning */
  1615. #pragma msg 147 warning
  1616.  
  1617.        putreg(REG_A0, (long)cmd);
  1618.        putreg(REG_D0, strlen(cmd));
  1619. #pragma msg 154 ignore
  1620.        *retvalue = (func)(); /* generates warning */
  1621. #pragma msg 154 warning
  1622.      } else /* version 36 or up! */
  1623.        *retvalue = RunCommand(seglist, 4000, cmd, strlen(cmd));
  1624.  
  1625.      UnLoadSeg( seglist );
  1626.    } else {
  1627.      /* we failed loading the command! */
  1628.      *retvalue = FUNCLIB_LOAD;
  1629.    }
  1630.  
  1631.    FREE(cmd);
  1632.  
  1633.    if(!*retvalue) {
  1634.       GETMEMA(namelist, sizeof(struct FuncList));
  1635.       STRDUPA(name, lib);
  1636.       namelist->name = name;
  1637.       namelist->opens = 1;
  1638.       namelist->flags = flags;
  1639.       namelist->next = scr->funclibs;
  1640.       scr->funclibs = namelist;
  1641.    }
  1642.    return FPL_OK;
  1643. }
  1644.  
  1645. ReturnCode REGARGS
  1646. CloseLib(struct Data *scr,
  1647.          uchar *lib,        /* funclib name or NULL for all */
  1648.          long flags,       /* options */
  1649.          long *retvalue)   /* funclib return code */
  1650. {
  1651.    struct MyLibrary *library;
  1652.    struct Library *DOSBase;
  1653.    struct FuncList *namelist=scr->funclibs;
  1654.    struct FuncList *prevlist=NULL;
  1655.    struct FuncList *next;
  1656.    uchar *command;
  1657.    uchar *cmd;
  1658.    ReturnCode ret;
  1659.    struct fplStr *string;
  1660.    BPTR seglist;
  1661.  
  1662.    struct ExecBase *SysBase = *(struct ExecBase **)4;
  1663.  
  1664.    library = (struct MyLibrary *)getreg(REG_A6);
  1665.    DOSBase = library->ml_DosBase;
  1666.  
  1667.    GETMEM(command, FPLLIB_MAXSPACE);
  1668.    cmd = command;
  1669.  
  1670.    while(namelist) {
  1671.      if(namelist->flags&FPLLIB_KEEP && namelist->opens==1) {
  1672.        /* This funclib is prevented from being 'soft' closed! */
  1673.        namelist->opens++;
  1674.      }
  1675.      if((!lib || !strcmp(namelist->name, lib)) &&
  1676.         (!--namelist->opens || flags&FPLLIB_FORCE) ) {
  1677.        /* the funclib _is_ opened! */
  1678.  
  1679.        strcpy(command, FPLLIB_SOURCE);
  1680.        strcpy(command+strlen(FPLLIB_SOURCE), lib);
  1681.        seglist = LoadSeg(command); /* load the command! */
  1682.        if(seglist) {
  1683.          strcpy(command, FPLLIB_CLOSECMD);
  1684.          command += strlen(FPLLIB_CLOSECMD);
  1685.     
  1686.          CALL(Ltostr(scr, &string, 10, (long)scr));
  1687.          strcpy(command, string->string);
  1688.          command[string->len]= '\n';   /* add newline */
  1689.          command[string->len+1]= '\0'; /* zero terminate */
  1690.          FREE(string);
  1691.     
  1692.          if(SysBase->SoftVer<36) {
  1693.            /* V33 solution! */
  1694.            uchar *segment = BADDR(seglist);
  1695.            int (*func)();
  1696. #pragma msg 147 ignore
  1697.            func = segment + 4; /* generates warning */
  1698. #pragma msg 147 warning
  1699.     
  1700.            putreg(REG_A0, (long)cmd);
  1701.            putreg(REG_D0, strlen(cmd));
  1702. #pragma msg 154 ignore
  1703.            *retvalue = (func)(); /* generates warning */
  1704. #pragma msg 154 warning
  1705.          } else /* version 36 or up! */
  1706.            *retvalue = RunCommand(seglist, 4000, cmd, strlen(cmd));
  1707.     
  1708.          UnLoadSeg( seglist );
  1709.        } else {
  1710.          /* we failed loading the command! */
  1711.          *retvalue = FUNCLIB_LOAD;
  1712.        }
  1713.     
  1714.     
  1715.        if(!*retvalue) {
  1716.          next = namelist->next;
  1717.          if(prevlist) /* was there a previous funclib in the list? */
  1718.            prevlist->next=next; /* point it to the next in the list */
  1719.          else
  1720.            scr->funclibs = next; /* point the origin to the next */
  1721.          FREEA(namelist->name); /* free name space */
  1722.          FREEA(namelist);       /* free struct */
  1723.          namelist = next;
  1724.          continue;
  1725.        }
  1726.      }
  1727.      prevlist = namelist;
  1728.      namelist = namelist->next;
  1729.    }
  1730.  
  1731.    FREE(cmd);
  1732.  
  1733.    return FPL_OK;
  1734. }
  1735.  
  1736. #endif
  1737.  
  1738. /**********************************************************************
  1739.  *
  1740.  * fplLtostr()
  1741.  *
  1742.  * Frontend to the FPL Ltostr() function to be used by anyone! The returned
  1743.  * string *must* be freed using the fplFreeString() function!
  1744.  *
  1745.  ****/
  1746.  
  1747. uchar * PREFIX
  1748. fplLtostr(AREG(0) struct Data *scr,
  1749.           DREG(0) long base,
  1750.           DREG(1) long num)
  1751. {
  1752.   ReturnCode ret;
  1753.   struct fplStr *string;
  1754.   uchar *retstring=NULL;
  1755. #ifdef DEBUGMAIL
  1756.   DebugMail(scr, MAIL_FUNCTION, 500, (void *)"fplLtostr");
  1757. #endif
  1758.   ret = Ltostr(scr, &string, base, num);
  1759.   if(FPL_OK == ret) {
  1760.     SwapMem(scr, string, MALLOC_STATIC); /* turn allocation to static type */
  1761.     retstring = string->string; /* return string pointer */
  1762.   }
  1763.   return retstring;
  1764. }
  1765.  
  1766.  
  1767. static ReturnCode REGARGS
  1768. Ltostr(struct Data *scr,
  1769.        struct fplStr **string,
  1770.        long base,
  1771.        long num)
  1772. {
  1773.   /*
  1774.    * Convert the integer to string with `any base'-convertions.
  1775.    */
  1776.   extern const uchar upper_digits[];
  1777.   ReturnCode ret;
  1778.   static const uchar *digits = upper_digits;
  1779.   long is_neg=num<0;
  1780.   long len=0;
  1781.   uchar buffer[34+sizeof(struct fplStr)];
  1782.   uchar *bufpoint;  /* the accurate position in the buffer */
  1783.  
  1784.   if(base>strlen(digits)) {
  1785.     CALL(Warn(scr, FPLERR_OUT_OF_REACH));
  1786.     num=strlen(digits); /* reset to maximum */
  1787.   }
  1788.   num=ABS(num);
  1789.     
  1790.   buffer[33+sizeof(struct fplStr)]=CHAR_ASCII_ZERO; /* zero byte termination */
  1791.   bufpoint=&buffer[33+sizeof(struct fplStr)]; /* start digit output position */
  1792.     
  1793.   if(num) {
  1794.     while(num>0) {
  1795.       *--bufpoint= digits[num % base];
  1796.       num /= base;
  1797.       len++;
  1798.     }
  1799.     if(is_neg) {
  1800.       *--bufpoint='-';
  1801.       len++;
  1802.     }
  1803.   } else {
  1804.     *--bufpoint=CHAR_ZERO;
  1805.     len++;
  1806.   }
  1807.  
  1808.   GETMEM(*string, len+sizeof(struct fplStr));
  1809.   strcpy((*string)->string, bufpoint);
  1810.   (*string)->len=len;
  1811.   (*string)->alloc=len;
  1812.   return(FPL_OK);
  1813. }
  1814.  
  1815. /**********************************************************************
  1816.  *
  1817.  * fplStrtol()
  1818.  *
  1819.  * Frontend to the FPL Strtol() function to be used by anyone!
  1820.  *
  1821.  ****/
  1822.  
  1823. long PREFIX
  1824. fplStrtol(AREG(0) uchar *string,
  1825.           DREG(0) long base,
  1826.           AREG(1) uchar **end)
  1827. {
  1828.   uchar *dummypoint;
  1829.   if(!end)
  1830.     end = &dummypoint;
  1831.   /*
  1832.    * THIS CAN'T CURRENT CALL DEBUGMAIL SINCE NO struct Data IS USED!!!
  1833.    */
  1834.   return Strtol(string, base, end);
  1835. }
  1836.  
  1837. /**********************************************************************
  1838.  *
  1839.  * Strtol()
  1840.  *
  1841.  * String to long integer. Code copied and changed from the GNU libc
  1842.  * source code package.
  1843.  *
  1844.  ****/
  1845.  
  1846. long REGARGS
  1847. Strtol(uchar *nptr,
  1848.        long base,
  1849.        uchar **end)
  1850. {
  1851.   uchar negative;
  1852.   unsigned long cutoff;
  1853.   unsigned long cutlim;
  1854.   long i;
  1855.   uchar *s;
  1856.   uchar c;
  1857.   uchar *save;
  1858.   long overflow;
  1859.  
  1860.   if (base < 0 || base == 1 || base > 36)
  1861.     base = 10;
  1862.  
  1863.   s = nptr;
  1864.  
  1865.   /* Skip white space.  */
  1866.   while(isspace(*s))
  1867.       s++;
  1868.  
  1869.   if (*s == CHAR_ASCII_ZERO)
  1870.     return (0);
  1871.  
  1872.   /* Check for a sign.  */
  1873.   else if (*s == CHAR_MINUS) {
  1874.     negative = 1;
  1875.     ++s;
  1876.   } else if (*s == CHAR_PLUS) {
  1877.     negative = 0;
  1878.     ++s;
  1879.   } else
  1880.     negative = 0;
  1881.  
  1882.   if ((base == 16 && s[0] == CHAR_ZERO && UPPER(s[1]) == CHAR_UPPER_X) ||
  1883.       (base == 2 && s[0] == CHAR_ZERO && UPPER(s[1]) == CHAR_UPPER_B) )
  1884.     s += 2;
  1885.  
  1886.   /* If BASE is zero, figure it out ourselves.  */
  1887.   if (base == 0)
  1888.     if (*s == '0') {
  1889.       switch(UPPER(s[1])) {
  1890.       case CHAR_UPPER_X:
  1891.     s += 2;
  1892.     base = 16;
  1893.     break;
  1894.       case CHAR_UPPER_B:
  1895.     s += 2;
  1896.     base = 2;
  1897.     break;
  1898.       default:
  1899.     base = 8;
  1900.     break;
  1901.       }
  1902.     } else
  1903.       base = 10;
  1904.  
  1905.   /* Save the pointer so we can check later if anything happened.  */
  1906.   save = s;
  1907.  
  1908.   cutoff = ULONG_MAX / (unsigned long int) base;
  1909.   cutlim = ULONG_MAX % (unsigned long int) base;
  1910.  
  1911.   overflow = 0;
  1912.   i = 0;
  1913.   for (c = *s; c; c = *++s) {
  1914.     if (isdigit(c))
  1915.       c -= '0';
  1916.     else if (isalpha(c))
  1917.       c = UPPER(c) - CHAR_UPPER_A + 10;
  1918.     else
  1919.       break;
  1920.     if (c >= base)
  1921.       break;
  1922.     /* Check for overflow.  */
  1923.     if (i > cutoff || (i == cutoff && c > cutlim))
  1924.       overflow = 1;
  1925.     else {
  1926.       i *= (unsigned long int) base;
  1927.       i += c;
  1928.     }
  1929.   }
  1930.  
  1931.   *end=s; /* this is the end position of the number */
  1932.  
  1933.   /* Check if anything actually happened.  */
  1934.   if (s == save)
  1935.     return (0);
  1936.  
  1937.   /* Check for a value that is within the range of
  1938.      `unsigned long int', but outside the range of `long int'.  */
  1939.   if (i > (negative ?
  1940.        - (unsigned long int) LONG_MIN :
  1941.        (unsigned long int) LONG_MAX))
  1942.     overflow = 1;
  1943.  
  1944.   if (overflow)
  1945.     return negative ? LONG_MIN : LONG_MAX;
  1946.  
  1947.   /* Return the result of the appropriate sign.  */
  1948.   return (negative ? - i : i);
  1949. }
  1950.  
  1951. /*****************************************************************************
  1952.  *
  1953.  * my_memicmp()
  1954.  *
  1955.  * This makes a case insensitive memcmp() with the very same parameters.
  1956.  *
  1957.  *********/
  1958.  
  1959. static long REGARGS my_memicmp(uchar *s1, uchar *s2, long len)
  1960. {
  1961.   long pos=0;
  1962.   long result;
  1963.   while(pos < len) {
  1964.     result = tolower(s1[pos]) - tolower(s2[pos]);
  1965.     if(result)
  1966.       return result;
  1967.     pos++;
  1968.   }
  1969.   return 0;
  1970. }
  1971.  
  1972.  
  1973. ReturnCode REGARGS
  1974.   StringExpr(struct Expr *val,        /* original string -> new */
  1975.          struct Data *scr)        /* standard */
  1976. {
  1977.   ReturnCode ret;
  1978.   struct fplStr *whole;
  1979.  
  1980.   CALL(Eat(scr)); /* scan white spaces */
  1981.  
  1982.   if(CHAR_PLUS == scr->text[0]) {
  1983.     
  1984.     GETMEM(whole, sizeof(struct fplStr));
  1985.     memset(whole, 0, sizeof(struct fplStr));
  1986.     
  1987.     /* put string in new string variable */
  1988.     CALL(StrAssign(val->val.str, scr, &whole, 1));
  1989.     
  1990.     while(CHAR_PLUS == scr->text[0]) {
  1991.       scr->text++; /* skip the '+' */
  1992.       
  1993.       CALL(Expression(val, scr, CON_STRING, NULL));
  1994.       
  1995.       /* append string to that new variable */
  1996.       CALL(StrAssign(val->val.str, scr, &whole, 1));
  1997.       
  1998.       if(!(val->flags&FPL_NOFREE) && val->val.str)
  1999.     FREE(val->val.str);
  2000.     }
  2001.       
  2002.     val->val.str = whole; /* get the string info! */
  2003.     val->flags&=~FPL_NOFREE; /* free this, yes! */
  2004.   }
  2005.   return FPL_OK;
  2006. }
  2007.